import { SnapObject } from "./object.js";
import { Sprite } from "./sprite.js";

class Stage extends SnapObject {
    constructor(scene) {
        super(scene);

        this.name = "Stage";
        this.scene = scene;

        this.sprites = [];

        this.width = 480;
        this.height = 360;

        this.trails = document.createElement("canvas");
        this.trailsCtx = this.trails.getContext("2d");
        this.trails.width = this.width;
        this.trails.height = this.height;

        var self = this;
        this.scriptEnv.globals = scene.variables;
        
        this.scriptEnv.broadcast = function(msg) {
            self.broadcast("message", msg);
        }

        this.scriptEnv.broadcastWait = function*(msg) {
            yield* self.broadcastSync("message", msg);
        }

        this.scriptEnv.penClear = function() {
            self.clear();
        }
    }

    async loadXml(xml) {
        if (xml.nodeName !== "stage") throw new Error("Expected <stage> tag");
        await super.loadXml(xml);

        // store temporary sprites list
        // this will then be sorted by sprite's idx attribute
        var sprites = [];

        for (let child of xml.children) {
            switch (child.nodeName) {
                case "sprites": {
                    for (let spriteNode of child.children) {
                        if (spriteNode.nodeName === "sprite") {
                            var sprite = new Sprite(this);
                            sprite.loadXml(spriteNode);
                            sprites.push([sprite, +spriteNode.attributes.idx.nodeValue]);
                        }
                    }

                    break;
                }
            }
        }

        sprites.sort((a, b) => a[1] - b[1]);
        this.sprites = sprites.map(v => v[0]);
    }

    setSize(w, h) {
        this.width = w;
        this.height = h;

        this.trails.width = w;
        this.trails.height = h;
    }

    clear() {
        var canvas = this.trails;
        var ctx = this.trailsCtx;

        ctx.clearRect(0, 0, this.width, this.height);
    }

    drawLine(x0, y0, x1, y1, color, size) {
        // don't draw line if start and end pos are the same
        if (x0 === x1 && y0 === y1) return;

        var ctx = this.trailsCtx;

        ctx.strokeStyle = color.toRGBString();
        ctx.lineWidth = size;
        ctx.lineCap = "round";

        ctx.beginPath();
        ctx.moveTo(x0 + this.width / 2, -y0 + this.height / 2);
        ctx.lineTo(x1 + this.width / 2, -y1 + this.height / 2);
        ctx.stroke();
        ctx.closePath();
    }

    getProject() {
        return this.scene.project;
    }

    getSprite(name) {
        this.sprites.find(v => v.name === name);
    }

    broadcast(type, name, ...args) {
        this.fireEvent(type, name, ...args);

        for (let sprite of this.sprites) {
            sprite.fireEvent(type, name, ...args);
        }
    }

    *broadcastSync(type, name) {
        yield* this.fireEventSync(type, name);

        for (let sprite of this.sprites) {
            yield* sprite.fireEventSync(type, name);
        }
    }

    render(canvas, ctx) {
        if (!this.shown) return;

        ctx.fillStyle = "white";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        
        ctx.save();

        this.applyEffects(ctx);

        if (this.curCostume) {
            ctx.drawImage(this.curCostume.canvas, this.width / 2 - this.curCostume.width / 2, this.height / 2 - this.curCostume.height / 2);
        }

        ctx.drawImage(this.trails, 0, 0);

        ctx.translate(this.width / 2, this.height / 2);

        for (let sprite of this.sprites) {
            sprite.render(canvas, ctx);
        }
        
        ctx.restore();
    }
}

export { Stage }